package com.briup.sm2;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.codec.digest.DigestUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import java.io.BufferedReader;
import java.io.DataOutputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.text.SimpleDateFormat;
import java.util.Map;
import java.util.TreeMap;
import java.util.UUID;

/**
 * @Description 单点登录场景下，token校验demo
 *
 *           <dependency>
 *               <groupId>commons-codec</groupId>
 *               <artifactId>commons-codec</artifactId>
 *               <version>1.15</version>
 *           </dependency>
 */
public class TokenValidateDemo {
    private static Logger logger = LoggerFactory.getLogger(TokenValidateDemo.class);

    // 移动认证平台 现网环境url
    //private static String tokenValidateUrl = "https://token.cmpassport.com:8300/uniapi/uniTokenValidate";

    // 移动认证平台 测试环境url
    private static String tokenValidateUrl = "http://120.197.235.102/api/uniTokenValidate";

    final private static SimpleDateFormat dateformat = new SimpleDateFormat("yyyyMMddHHmmssSSS");

    // 根据实质情况调整超时时间，单位：毫秒
    private static final int HTTP_TIME_OUT = 3000;
    private static final int HTTP_READ_TIMEOUT_OUT = 6000;

    /**
     * 业务A --> 业务B,  在 单点登录的场景中，
     * 业务A负责请求 UA02003接口，A传递token给B,
     * 业务B负责请求统一token校验接口
     *
     **/
    public static void main(String[] args) throws Exception {
        System.out.println("----- start ----- ");
        // TODO token来源于业务A, 请使用有效的token
        String token = "YZsidssolg54beabd9b09447a0592ede680ca79cca";
        // TODO 请使用 移动认证平台 分配给 业务B 的sourceid 和 sourcekey
        String sourceid = "007011";
        String sourcekey = "eCaA40cPxU4Qn5Sa";

        JSONObject data = tokenValidate(sourceid, sourcekey, token);

        if (data != null && !data.isEmpty()) {
            System.out.println("data = " + data.toJSONString());
            System.out.println("usessionid = " + data.getString("usessionid"));
            System.out.println("手机号掩码msisdnmask = " + data.getString("msisdnmask"));

            String msisdn = data.getString("msisdn");
            System.out.println("密文手机号: " + msisdn);
            String phone = deCodeAES(msisdn, sourcekey);
            System.out.println("明文手机号: " + phone);
        }

    }


    public static JSONObject tokenValidate(String sourceid, String sourcekey, String token) {
        logger.info("token={}", token);
        String resp = null;
        try {
            String appType = "2";
            String msgid = getUUID();
            JSONObject requestData = getRequestData(appType, msgid, token, sourceid, sourcekey);
            resp = post(tokenValidateUrl, requestData.toJSONString(), StandardCharsets.UTF_8.name(), false);
        } catch (Exception e) {
            logger.error("异常:", e);
        }
        logger.info("resp= {}", resp);
        JSONObject parseObject = JSON.parseObject(resp);
        JSONObject header = (JSONObject) parseObject.get("header");
        String resultcode = (String) header.get("resultcode");
        String resultdesc = header.getString("resultdesc");
        if (!"103000".equals(resultcode)) {
            // TODO 处理异常
            //throw new RuntimeException("处理异常 返回码");
        }
        JSONObject body = (JSONObject) parseObject.get("body");
        return body;
    }


    private static JSONObject getRequestData(String appType, String msgId, String token, String sourceid
            , String sourcekey) {
        // 单点登录场景下，必须使用 version = "1.0"
        String version = "1.0";
        // 单点登录场景下，必须使用sourceid,  所以idtype = "0"
        String idtype = "0";
        // systemtime 是17个数字， 示例："20220126151054040"
        String systemTime = dateformat.format(System.currentTimeMillis());

        Map<String, Object> map = new TreeMap<String, Object>(); // 用treeMap按照key做排序
        map.put("version", version);
        map.put("id", sourceid);
        map.put("idtype", idtype);
        map.put("msgid", msgId);
        map.put("key", sourcekey);//only at create sign requied
        map.put("apptype", appType);
        map.put("systemtime", systemTime);
        map.put("token", token);
        String enStr = mapToString(map);
        String sign = md5(enStr);

        JSONObject header = new JSONObject();
        header.put("version", version);
        header.put("id", sourceid);
        header.put("idtype", idtype);
        header.put("msgid", msgId);
        header.put("apptype", appType);
        header.put("systemtime", systemTime);
        header.put("sign", sign);
        header.put("expandparams", "300");

        JSONObject body = new JSONObject();
        body.put("token", token);
        // body.put("userInformation", userInformation); //jssdk token校验时（必填）

        JSONObject requestData = new JSONObject();
        requestData.put("header", header);
        requestData.put("body", body);
        return requestData;

    }

    private static String getUUID() {
        String uuidStr = UUID.randomUUID().toString().replaceAll("-", "");
        return uuidStr;
    }

    private static String mapToString(Map<String, Object> map) {
        if (null == map || map.isEmpty()) {
            return null;
        }

        StringBuilder sb = new StringBuilder();
        for (Map.Entry<String, Object> entry : map.entrySet()) {
            sb.append(entry.getValue());
        }

        return sb.toString();
    }

    private static String md5(String text) {
        return DigestUtils.md5Hex(text);
    }


    /**
     * @param httpUrl:
     * @param params:
     * @param charset:
     * @param encode:
     * @Description: 发送http的post 请求
     * @return: java.lang.String
     **/
    public static String post(String httpUrl, String params, String charset, boolean encode) {
        // TODO 下面代码仅供参考，建议：尽量使用你们项目中 已经存在的常用的功能相同的方法

        long startTime = System.currentTimeMillis();
        URL url = null;
        HttpURLConnection htpConn = null;
        String result = "";
        try {
            logger.info("httpUrl = {}, params = {}", httpUrl, params);
            url = new URL(httpUrl);
            htpConn = (HttpURLConnection) url.openConnection();
            htpConn.setDoOutput(true);
            htpConn.setDoInput(true);
            htpConn.setRequestMethod("POST");
            // Post请求不能使用缓存
            htpConn.setUseCaches(false);
            htpConn.setConnectTimeout(HTTP_TIME_OUT);
            htpConn.setReadTimeout(HTTP_READ_TIMEOUT_OUT);
            htpConn.setRequestProperty("Content-Type", "application/json");
            htpConn.connect();
            // 是否需要编码
            if (encode) {
                params = URLEncoder.encode(params, charset);
            }
            DataOutputStream out = new DataOutputStream(htpConn.getOutputStream());
            out.write(params.getBytes(charset));
            out.flush();
            out.close();

//			int code = htpConn.getResponseCode();
//			System.out.println("encode=" + encode);
//			System.out.println("code=" + code);

            // 读返回值
            BufferedReader reader = new BufferedReader(new InputStreamReader(htpConn.getInputStream(), charset));
            String line;
            while ((line = reader.readLine()) != null) {
                result = result + line;
            }
            logger.info("result:{}", result);
            reader.close();
            htpConn.disconnect();
        } catch (Exception e) {
            logger.error("post请求异常,httpUrl:{},参数:{}", httpUrl, params, e);
        } finally {
            if (htpConn != null) {
                htpConn.disconnect();
            }
        }
        long costTime = System.currentTimeMillis() - startTime;
        logger.info("当前请求所花费时间：" + costTime + "毫秒");
        return result;
    }

    public static String deCodeAES(String password, String key) throws Exception {
        byte[] keybBytes = DigestUtils.md5(key);
        byte[] debase64Bytes = Base64.decodeBase64(password.getBytes());
        return new String(decrypt(debase64Bytes, keybBytes));
    }

    private static byte[] decrypt(byte[] text, byte[] key) throws Exception {
        SecretKeySpec aesKey = new SecretKeySpec(key, "AES");
        Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
        cipher.init(Cipher.DECRYPT_MODE, aesKey);
        return cipher.doFinal(text);
    }

}
